#pragma once
#include <Windows.h>

#include <string>
#include <fstream>
#include <iostream>
#include <chrono>
#include <iomanip>
#include <mutex>
/*
Logger
Output the log information into the console or file
Uses the template to achive the printf format.
Support four level:Info,Warning,Debug,Error
*/
namespace BackupFS 
{
#define Logger BackupFSLogger::GetInstance()
#define DebugLogFunction	Logger->Debug(__FILE__, ":",__LINE__,",", __FUNCTION__)
#define DebugLogFunctionWithFileName	Logger->Debug(__FILE__, ":",__LINE__,",", __FUNCTION__,",FileName ",W2S(filename))

	enum
	{
		LevelInfo = 1,
		LevelWarning = 2,
		LevelError = 4,
		LevelDebug = 8,
		LevelAll = 0xFF
	};
	class BackupFSLogger {
	protected:
		BackupFSLogger();
		virtual ~BackupFSLogger();
	public:
		static BackupFSLogger* GetInstance()
		{
			std::lock_guard<std::recursive_mutex> Locker(_Locker);
			if (_Instance == nullptr) {
				_Instance = new BackupFSLogger();
				_Instance->Info("Support Info Level");
				_Instance->Debug("Support Debug Level");
				_Instance->Warn("Support Warn Level");
				_Instance->Error("Support Error Level");
			}
			return _Instance;
		}
		static void ReleaseInstance();
	public:
		/*
		Error log,output the text to the console with RED color
		*/
		template<typename... Args>
		void Error(Args&&... args)
		{
			if (_LogLevel & LevelError)
			{
				std::lock_guard<std::recursive_mutex> Locker(_Locker);
				_SetColor(FOREGROUND_RED, 0);
				_Log(" [Error] ",std::forward<Args>(args)...);
				_SetColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, 0);
			}
		}
		/*
		Info log,output the text to the console with GREEN color
		*/
		template<typename... Args>
		void Info(Args&&... args)
		{
			if (_LogLevel & LevelInfo)
			{
				std::lock_guard<std::recursive_mutex> Locker(_Locker);
				_SetColor(FOREGROUND_GREEN, 0);
				_Log(" [Info] ",std::forward<Args>(args)...);
				_SetColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, 0);
			}
		}
		/*
		Warning log,output the text to the console with INTENSITY color
		*/
		template<typename... Args>
		void Warn(Args&&... args) 
		{
			if (_LogLevel & LevelWarning)
			{
				std::lock_guard<std::recursive_mutex> Locker(_Locker);
				_SetColor(FOREGROUND_INTENSITY, 0);
				_Log(" [Warning] ",std::forward<Args>(args)...);
				_SetColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, 0);
			}
		}
		/*
		Debug log,output the text to the console with BLUE color
		*/
		template<typename... Args>
		void Debug(Args&&... args)
		{
			if (_LogLevel & LevelDebug)
			{
				std::lock_guard<std::recursive_mutex> Locker(_Locker);
				_SetColor(FOREGROUND_BLUE, 0);
				_Log(" [Debug] ",std::forward<Args>(args)...);
				_SetColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, 0);
			}
		}
		void SetLogTimeStamp(bool LogTimeStamp) { _LogTimeStamp = LogTimeStamp; }
		void SetLogLevel(int LogLevel) { _LogLevel = LogLevel; }
		bool SetLogFile(const std::wstring& LogFileName);
		void SetToConsole(bool ToConsole);
		
	private:
		void _SetColor(int text_color, int bg_color);
		void _Log_To_Console();
		void _Log_To_File();
		template<typename First, typename... Rest>
		void _Log_To_Console(First&& first, Rest&&... rest)
		{
			std::cout << first;
			_Log_To_Console(std::forward<Rest>(rest)...);
		}
		template<typename First, typename... Rest>
		void _Log_To_File(First&& first, Rest&&... rest)
		{
			if (_LogFile.is_open())
			{
				_LogFile << first;
			}
			_Log_To_File(std::forward<Rest>(rest)...);
		}
		template<typename First, typename... Rest>
		void _Log(First&& first, Rest&&... rest)
		{
			if (!_ToFile && !_ToConsole)
			{
				return;
			}
			auto now = std::chrono::system_clock::now();
			std::time_t now_c = std::chrono::system_clock::to_time_t(now);
			std::tm local_tm;
			localtime_s(&local_tm, &now_c);

			if (_ToConsole)
			{
				if (_LogTimeStamp)
				{
					std::cout << "[ " << std::put_time(&local_tm, "%Y-%m-%d %H:%M:%S") << " ] ";
				}
				std::cout << first;
				_Log_To_Console(std::forward<Rest>(rest)...);
			}
			if (_ToFile && _LogFile.is_open())
			{
				if (_LogTimeStamp)
				{
					_LogFile << "[ " << std::put_time(&local_tm, "%Y-%m-%d %H:%M:%S") << " ] ";
				}
				_LogFile << first;
				_Log_To_File(std::forward<Rest>(rest)...);
			}
		}
		bool _OpenLogFile();
	private:
		bool _ToFile = true;
		bool _ToConsole = true;
		bool _LogTimeStamp = true;
		std::wstring _LogFileName;
		std::ofstream _LogFile;
		static std::recursive_mutex _Locker;
		static BackupFSLogger* _Instance;
		int _LogLevel = LevelAll;
	};
}